package store.lunangangster.engine.impl.query;

import java.util.List;
import store.lunangangster.engine.NodeFlowException;
import store.lunangangster.engine.impl.Context;
import store.lunangangster.engine.impl.cmd.Command;
import store.lunangangster.engine.impl.cmd.CommandExecutor;
import store.lunangangster.engine.impl.interceptor.CommandContext;

/**
 * 该类继承了Command类，每次Query都会视作一个Command的execute，所以需要在CommandContext的监管下进行，
 * 包括事务、监听器等。
 *
 * <p>AbstractQuery既是一个Query又是一个Command。要么该对象是作为Command被CommandExecutor执行、
 * 要么在某个Command中，使用其CommandContext中的Manager进行相应操作
 *
 * <ul>
 *   <li>作为Query对象，可以在某个Command的执行过程中，获取CommandContext进行数据库操作</li>
 *   <li>作为Command对象，可以交给CommandExecutor进行执行，从而通过一系列的CommandInterceptor</li>
 * </ul>
 *
 * @author cwk
 * @version 1.0
 */
public abstract class AbstractQuery<T extends Query<?, ?>, U> implements Query<T, U>, Command<Object> {

  protected String orderBy;
  protected QueryResultType resultType;

  protected transient OrderProperty orderProperty;
  protected transient CommandContext commandContext;
  protected transient CommandExecutor executor;

  public AbstractQuery(CommandExecutor executor) {
    this.executor = executor;
    check();
  }

  private void check() {
    if (executor == null || (commandContext = Context.getCommandContext()) == null || commandContext.isClose()) {
      throw new NodeFlowException("Query对象的创建必须在某个Command的execute过程中并且提供CommandExecutor");
    }
  }


  @Override
  public T orderBy(OrderProperty orderProperty) {
    this.orderProperty = orderProperty;
    return (T) this;
  }

  @Override
  public T desc() {
    if (orderProperty == null) {
      throw new NodeFlowException("使用desc方法之前必须使用orderBy方法");
    }
    addOrder(orderProperty, Direction.DESC);
    return (T) this;
  }

  @Override
  public T asc() {
    if (orderProperty == null) {
      throw new NodeFlowException("使用asc方法之前必须使用orderBy方法");
    }
    addOrder(orderProperty, Direction.ASC);
    return (T) this;
  }

  private void addOrder(OrderProperty orderProperty, Direction direction) {
    if (orderBy == null) {
      orderBy = "";
    } else {
      orderBy = orderBy + ", ";
    }
    String columnName = orderProperty.getName();
    String order = direction.getName();
    orderBy = orderBy + columnName + " " + order;
  }

  @Override
  public U one() {
    List<U> list = list();
    if (list.isEmpty()) {
      return null;
    }
    return list.get(0);
  }

  @Override
  public List<U> list() {
    resultType = QueryResultType.LIST;
    if (executor != null) {
      return (List<U>) executor.execute(this);   // 当前的Query对象作为Command进行执行，不依赖其他Command
    } else {
      return queryList(Context.getCommandContext());       // 当前的Query对象依赖其他的Command，在其他Command中的execute中进行查询
    }
  }

  @Override
  public Page<U> page() {
    resultType = QueryResultType.PAGE;
    if (executor != null) {
      return (Page<U>) executor.execute(this);
    } else {
      return queryPage(Context.getCommandContext());
    }
  }

  @Override
  public int count() {
    return 0;
  }

  @Override
  public Object execute(CommandContext commandContext) {
    switch (resultType) {
      case LIST:
        return queryList(commandContext);
      case PAGE:
        return queryPage(commandContext);
      case INT:
        return queryCount(commandContext);
    }
    return null;
  }

  public String getOrderBy() {
    return orderBy;
  }

  protected abstract List<U> queryList(CommandContext commandContext);
  protected abstract Page<U> queryPage(CommandContext commandContext);
  protected abstract int queryCount(CommandContext commandContext);

  protected static enum QueryResultType {
    LIST,
    PAGE,
    INT;
  }

  private static enum Direction {
    ASC("asc"),
    DESC("desc");

    private final String name;

    Direction(String name) {
      this.name = name;
    }

    public String getName() {
      return name;
    }
  }
}
